package cn.edu.hnu.gpg.handler;

import cn.edu.hnu.gpg.api.error.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;

/**
 * 拦截定义的 16 类 API 错误异常，返回相应的 HTTP 状态码，
 * 将异常中的 {@code error} 属性映射到响应体中。
 *
 * Spring MVC 的控制器中当参数不符合条件时抛出的异常，如
 * {@code MissingServletRequestParameterException}
 * 也应在此被拦截并转化为 {@code InvalidArgumentException}
 */
@ControllerAdvice
public class ApiExceptionHandler {

    private static final Logger logger = LogManager.getLogger(ApiExceptionHandler.class);

    @ExceptionHandler(value = UnknownException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiException.ResponseBody handleApiException(UnknownException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = InvalidArgumentException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiException.ResponseBody handleApiException(InvalidArgumentException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = DeadlineExceededException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.GATEWAY_TIMEOUT)
    public ApiException.ResponseBody handleApiException(DeadlineExceededException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = NotFoundException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ApiException.ResponseBody handleApiException(NotFoundException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = AlreadyExistsException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.CONFLICT)
    public ApiException.ResponseBody handleApiException(AlreadyExistsException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = PermissionDeniedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.FORBIDDEN)
    public ApiException.ResponseBody handleApiException(PermissionDeniedException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = UnauthenticatedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.UNAUTHORIZED)
    public ApiException.ResponseBody handleApiException(UnauthenticatedException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = ResourcesExhaustedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.TOO_MANY_REQUESTS)
    public ApiException.ResponseBody handleApiException(ResourcesExhaustedException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = FailedPreconditionException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.PRECONDITION_FAILED)
    public ApiException.ResponseBody handleApiException(FailedPreconditionException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = AbortedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.CONFLICT)
    public ApiException.ResponseBody handleApiException(AbortedException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = OutOfRangeException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiException.ResponseBody handleApiException(OutOfRangeException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = UnimplementedException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.NOT_IMPLEMENTED)
    public ApiException.ResponseBody handleApiException(UnimplementedException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = InternalException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiException.ResponseBody handleApiException(InternalException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = UnavailableException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.SERVICE_UNAVAILABLE)
    public ApiException.ResponseBody handleApiException(UnavailableException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = DataLossException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ApiException.ResponseBody handleApiException(DataLossException e) {
        logger.debug(e);
        return e.buildResponseBody();
    }

    @ExceptionHandler(value = MissingServletRequestParameterException.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ApiException.ResponseBody handleSpringMissingParameterException(MissingServletRequestParameterException e) {
        ApiError error = new ApiError(ApiError.ErrorType.ERR_MISSING_PARAMETER, e.getMessage());
        return handleApiException(new InvalidArgumentException(error));
    }
}
